﻿using Sustainalytics.BusinessResearchProductStates.Entities.ResearchState;
using Sustainalytics.ClientDW.DataAccessLayer;
using Sustainalytics.DataLayer;
using Sustainalytics.Synchronization.Interface;
using System;
using System.Collections.Generic;
using System.Configuration.Abstractions;
using System.Linq;
using System.Threading.Tasks;

namespace Sustainalytics.ScreeningTool.Synchronization
{
    public class ScreeningSyncStep : ISyncStep
    {
        public SyncStepOrder ExecutionOrder => SyncStepOrder.ScreeningData;

        public SyncContext Start(SyncContext syncContext)
        {
            try
            {
                Log.Initialize(syncContext.Log);
                syncContext.Token.ThrowIfCancellationRequested();

                Log.Execution("Screening Sync Step", () => Execute(syncContext)).Wait();

                syncContext.Token.ThrowIfCancellationRequested();
                syncContext.Status = SyncOperationStatus.Succeeded;
            }
            catch (AggregateException ex)
            {
                syncContext.Log(ex.Flatten().ToString());
                syncContext.Status = SyncOperationStatus.Failed;
                throw;
            }
            catch (Exception ex)
            {
                syncContext.Log(ex.ToString());
                syncContext.Status = SyncOperationStatus.Failed;
                throw;
            }

            return syncContext;
        }

        private async Task Execute(SyncContext syncContext)
        {
            var universesRepository = UniversesRepository.GetUniversesRepository(syncContext.GAConnectionStrings["ClientUniverses"]);
            var companyScreeningRepository = CompanyScreeningRepository.GetCompanyScreeningRepository(syncContext.GAConnectionStrings["ClientPlatform"]);

            companyScreeningRepository.PublishStandardFilters();

            await CreateScreeningDataAsync(syncContext);

            UpdateFilterData(universesRepository, companyScreeningRepository);
        }

        private static async Task CreateScreeningDataAsync(SyncContext syncContext)
        {
            var configurationManager = ConfigurationManager.Instance;

            var gAConnectionStrings = syncContext.GAConnectionStrings;
            await CreateScreeningDataAsync(
                ClientFacingProduct.GA,
                gAConnectionStrings,
                configurationManager)
                .ConfigureAwait(false);

            var syncDsoDatabase = configurationManager.AppSettings.AppSetting<bool>("SyncDsoDatabase");
            if (syncDsoDatabase)
            {
                var dsoConnectionStrings = GetDsoConnectionStrings(syncContext);
                await CreateScreeningDataAsync(
                    ClientFacingProduct.DS,
                    dsoConnectionStrings,
                    configurationManager)
                    .ConfigureAwait(false);
            }
        }

        private static Dictionary<string, string> GetDsoConnectionStrings(SyncContext syncContext)
        {
            var gAConnectionStrings = syncContext.GAConnectionStrings;
            var dsoConnectionStrings = syncContext.DSOConnectionStrings;

            gAConnectionStrings
                .Where(x => !dsoConnectionStrings.ContainsKey(x.Key))
                .ToList()
                .ForEach(x => dsoConnectionStrings.Add(x.Key, x.Value));

            return dsoConnectionStrings;
        }

        public static async Task CreateScreeningDataAsync(
            ClientFacingProduct clientFacingProduct,
            Dictionary<string, string> connectionStrings,
            IConfigurationManager configurationManager)
        {
            await Log.Execution($"Screening Data Sync for {clientFacingProduct}", async () =>
            {
                var container = SimpleInjectorConfig.Register(clientFacingProduct, connectionStrings, configurationManager);
                await Log.Execution("loading caches", () => container.InitializeCaches(clientFacingProduct)).ConfigureAwait(false);

                var screeningDataCreator = container.GetInstance<ScreeningDataCreator>();
                await Log.Execution("processing collections", () => screeningDataCreator.CreateScreeningDataAsync(clientFacingProduct)).ConfigureAwait(false);
            });
        }

        private static void UpdateFilterData(
            IUniversesRepository universesRepository,
            ICompanyScreeningRepository companyScreeningRepository)
        {
            var universesForAccounts = universesRepository.GetAllUniversesIdsAndAccountIds()
                .GroupBy(p => p.AccountId)
                .ToDictionary(k => k.Key, v => v.ToDictionary(kk => kk.Name, vv => vv.Id));
            var allFilters = companyScreeningRepository.GetAllFilters();
            companyScreeningRepository.DuplicateFiltersCollection();
            foreach (var filter in allFilters)
            {
                if (filter.UniverseName == null)
                    continue;

                if (!universesForAccounts.ContainsKey(filter.AccountId))
                    continue;

                if (!universesForAccounts[filter.AccountId].ContainsKey(filter.UniverseName))
                    continue;

                var newUniverseId = universesForAccounts[filter.AccountId][filter.UniverseName];

                companyScreeningRepository.UpdateFilter(filter.Id, newUniverseId);
            }

            companyScreeningRepository.CleanUpOldFiltersCollection();
        }
    }
}
